import { CheckAns } from "./CheckAns";

namespace LeetCode0002AddTwoNumbers {

    // Definition for singly-linked list.
    class ListNode {
        val: number
        next: ListNode | null
        constructor(val?: number, next?: ListNode | null) {
            this.val = (val === undefined ? 0 : val)
            this.next = (next === undefined ? null : next)
        }
    }

    // 直接计算
    function addTwoNumbers(l1: ListNode | null, l2: ListNode | null): ListNode | null {
        if (!l1 || !l2)
            return null;

        let l11: ListNode | null = l1;
        let l22: ListNode | null = l2;
        let nodes: Array<ListNode> = new Array();
        let jinwei: boolean = false;
        while (l11 || l22 || jinwei) {
            let vs: number = jinwei ? 1 : 0;
            if (l11) {
                vs += l11.val;
                l11 = l11.next;
            }
            if (l22) {
                vs += l22.val;
                l22 = l22.next;
            }
            jinwei = vs >= 10;
            vs = vs >= 10 ? (vs % 10) : vs;
            let temp: ListNode = new ListNode(vs, null);
            nodes.push(temp);
        }
        let tempList: ListNode | null = null;
        nodes.reverse().forEach((v: ListNode) => {
            v.next = tempList;
            tempList = v;
        });
        return tempList;
    };

    // 递归调用方法
    function addTwoNumbers1(l1: ListNode | null, l2: ListNode | null): ListNode | null {
        if (!l1 || !l2)
            return null;

        // function 递归调用方法
        const func: (l11: ListNode | null, l22: ListNode | null, jw: boolean) => ListNode | null = (l11: ListNode | null, l22: ListNode | null, jw: boolean): ListNode | null => {
            let vs: number = jw ? 1 : 0;
            let next1 = null;
            if (l11) {
                vs += l11.val;
                next1 = l11.next;
            }
            let next2 = null;
            if (l22) {
                vs += l22.val;
                next2 = l22.next;
            }
            let cjw = vs >= 10;
            vs = vs >= 10 ? (vs % 10) : vs;

            if (next1 || next2 || cjw) {
                return new ListNode(vs, func(next1, next2, cjw));
            } else {
                return new ListNode(vs);
            }
        };

        let ans: ListNode | null = func(l1, l2, false);
        return ans;
    };

    // 递归调用方法2 因为是引用传递的方式，且没有禁止修改传入参数的限制，直接修改l1
    function addTwoNumbers2(l1: ListNode | null, l2: ListNode | null): ListNode | null {
        // 强制l1链表为主线
        if (!l1) return null;

        // l1，l2？
        const vs: number = l1.val + (l2 ? l2.val : 0);
        l1.val = vs % 10;
        if (vs >= 10) {
            if (l1.next) {
                l1.next.val += 1;
            }
            else {
                l1.next = new ListNode(1);
            }
        }
        // 预处理下次调用
        if (!l1.next && l2 && l2.next) {
            l1.next = l2.next;
            l2 = null;
        }
        addTwoNumbers2(l1.next, l2 ? l2.next : null);

        // 返回主线l1链表头
        return l1;
    }

    // 辅助函数：数组转指定结构
    function ary2ListNode(ary: number[]): ListNode | null {
        if (!ary || ary.length == 0)
            return null;
        let temp: ListNode | null = null;
        for (let i = ary.length - 1; i >= 0; i--) {
            temp = new ListNode(ary[i], temp);
        }
        return temp;
    }
    // 辅助函数：指定结构转数组
    function listNode2Ary(ln: ListNode | null): number[] {
        let ary: number[] = new Array();
        let temp: ListNode | null = ln;
        while (temp) {
            ary.push(temp.val);
            temp = temp.next;
        }
        return ary;
    }

    // test
    const testData: { l1: number[], l2: number[], ans: number[] }[] = [
        { l1: [0], l2: [7, 3], ans: [7, 3] },
        { l1: [2, 4, 3], l2: [5, 6, 4], ans: [7, 0, 8] },
        { l1: [0], l2: [0], ans: [0] },
        { l1: [9, 9, 9, 9, 9, 9, 9], l2: [9, 9, 9, 9], ans: [8, 9, 9, 9, 0, 0, 0, 1] }
    ];
    for (const data of testData) {
        const ans = addTwoNumbers2(ary2ListNode(data.l1), ary2ListNode(data.l2));
        CheckAns(data, listNode2Ary(ans));
    }

}